package com.atguigu.gmall.payment.controller;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.model.enums.PaymentType;
import com.atguigu.gmall.model.payment.PaymentInfo;
import com.atguigu.gmall.payment.config.AlipayConfig;
import com.atguigu.gmall.payment.service.AlipayService;
import com.atguigu.gmall.payment.service.PaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.TimeUnit;

//  @RestController
@Controller
@RequestMapping("/api/payment/alipay")
public class AlipayApiController {

    @Autowired
    private AlipayService alipayService;

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${app_id}")
    private String app_id;

    //  http://api.gmall.com/api/payment/alipay/submit/48
    @GetMapping("/submit/{orderId}")
    @ResponseBody
    public String aliPay(@PathVariable Long orderId){
        try {
            //  返回数据.
            return alipayService.createAliPay(orderId);
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
    }

    //  http://api.gmall.com/api/payment/alipay/callback/return
    @GetMapping("/callback/return")
    public String callbackReturn(@RequestParam Map<String,String> paramsMap){
        //  获取outTradeNo
        String outTradeNo = paramsMap.get("out_trade_no");
        //  System.out.println(map); 更新交易记录状态.
        this.paymentService.updatePaymentInfoStatus(outTradeNo,PaymentType.ALIPAY.name(),paramsMap);
        //  可以做数据更新，数据校验.
        //  返回一个页面. http://payment.gmall.com/pay/success.html
        return "redirect:"+ AlipayConfig.return_order_url;
    }
    //  异步回调 http://9949xa.natappfree.cc/api/payment/alipay/callback/notify
    //  https: //商家网站通知地址?voucher_detail_list=[{"amount":"0.20","merchantContribute":"0.00","name":"5折券","otherContribute":"0.20","type":"ALIPAY_DISCOUNT_VOUCHER","voucherId":"2016101200073002586200003BQ4"}]&fund_bill_list=[{"amount":"0.80","fundChannel":"ALIPAYACCOUNT"},{"amount":"0.20","fundChannel":"MDISCOUNT"}]&subject=PC网站支付交易&trade_no=2016101221001004580200203978&gmt_create=2016-10-12 21:36:12&notify_type=trade_status_sync&total_amount=1.00&out_trade_no=mobile_rdm862016-10-12213600&invoice_amount=0.80&seller_id=2088201909970555&notify_time=2016-10-12 21:41:23&trade_status=TRADE_SUCCESS&gmt_payment=2016-10-12 21:37:19&receipt_amount=0.80&passback_params=passback_params123&buyer_id=2088102114562585&app_id=2016092101248425&notify_id=7676a2e1e4e737cff30015c4b7b55e3kh6& sign_type=RSA2&buyer_pay_amount=0.80&sign=***&point_amount=0.00
    @PostMapping("/callback/notify")
    @ResponseBody
    public String callbackNotify(@RequestParam Map<String,String> paramsMap){
        //        Map<String, String> paramsMap = ... //将异步通知中收到的所有参数都存放到map中
        //  验签：
        boolean signVerified = false; // 调用SDK验证签名
        try {
            signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        if(signVerified){
            // TODO 验签成功后，按照支付结果异步通知中的描述，对支付结果中的业务内容进行二次校验，校验成功后在response中返回success并继续商户自身业务处理，校验失败返回failure
            //  获取out_trade_no
            String outTradeNo = paramsMap.get("out_trade_no");
            String totalAmount = paramsMap.get("total_amount");
            String appId = paramsMap.get("app_id");
            String notifyId = paramsMap.get("notify_id");
            String tradeStatus = paramsMap.get("trade_status");
            //  1.  校验参数通过outTradeNo 查询数据是否存在. 存在有：不存在没有!
            PaymentInfo paymentInfoQuery = this.paymentService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name());
            //  校验outTradeNo 失败; paymentInfoQuery.getTotalAmount().compareTo(new BigDecimal(totalAmount))!=0
            //  0.01 比较; new BigDecimal("0.01")
            if (paymentInfoQuery==null || new BigDecimal("0.01").compareTo(new BigDecimal(totalAmount))!=0 ||
                !app_id.equals(appId)){
                return "failure";
            }
            //  2.  去重复  同一条信息，返回的notify_id 一样的！
            //  判断是否重复. hget key field;  setnx key value;
            Boolean result = redisTemplate.opsForValue().setIfAbsent(notifyId, notifyId, 10, TimeUnit.MINUTES);
            //  result = true   没人执行过， result = false 有人执行过.
            if (!result){
                //  停止执行.
                return "failure";
            }

            //  3.  TRADE_SUCCESS 或 TRADE_FINISHED 时，支付宝才会认定为买家付款成功
            if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)){
                //  更新直接交易记录的状态.
                if (!"PAID".equals(paymentInfoQuery.getPaymentStatus())){
                    //  paymentStatus = PAID; callback_time callback_content trade_no
                    //  修改订单状态：发送消息修改.
                    this.paymentService.updatePaymentInfoStatus(outTradeNo,PaymentType.ALIPAY.name(),paramsMap);
                }
                //  执行成功.
                return "success";
            }
        }else{
            // TODO 验签失败则记录异常日志，并在response中返回failure.
            return "failure";
        }
        return "failure";
    }

    //  退款
    @GetMapping("refund/{orderId}")
    @ResponseBody
    public Result refund(@PathVariable Long orderId){
        //  调用服务层方法.
        Boolean result = this.alipayService.refund(orderId);
        if (result){
            return Result.ok(result);
        }else {
            return Result.fail();
        }
    }

    //  关闭支付宝交易记录.
    @GetMapping("closePay/{orderId}")
    @ResponseBody
    public Boolean closePay(@PathVariable Long orderId){
        //  调用服务层方法
        return this.alipayService.closePay(orderId);
    }

    //  查看交易状态
    @RequestMapping("checkPayment/{orderId}")
    @ResponseBody
    public Boolean checkPayment(@PathVariable Long orderId){
        //  调用服务层方法.
        return this.alipayService.checkPayment(orderId);
    }

    //  封装查询交易记录方法.
    @GetMapping("getPaymentInfo/{outTradeNo}")
    @ResponseBody
    public PaymentInfo getPaymentInfo(@PathVariable String outTradeNo){
        PaymentInfo paymentInfo = paymentService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name());
        if (null!=paymentInfo){
            return paymentInfo;
        }
        return null;
    }
}
